﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Health;
using SDS.SPHealthAnalyzerRules.Delegation.Helpers;

namespace SDS.SPHealthAnalyzerRules.Delegation.Rules
{
    /// <summary>
    /// Ensures that each PerformancePoint Services service application is configured for constrained delegation WITH protocol transition
    /// </summary>
    /// <remarks>
    /// http://technet.microsoft.com/en-us/library/gg502603.aspx
    /// "To allow PerformancePoint services to delegate the clients identity Kerberos constrained delegation must be configured. It is required to configure constrained delegation with protocol transition for the conversion of claims token to windows token via the WIF C2WTS. "
    /// 
    /// 
    /// may at some point be worth checking that the service app instances are in the same proxy group as a webapp using kerb... but that'll be another day.
    /// </remarks>
    public class PerformancePointServicesAppPool_Using_ConstrainedDelegation : SPHealthAnalysisRule
    {
        private Guid CorrelationToken = Guid.NewGuid();
        List<string> Errors = new List<string>();
        List<string> Remedies = new List<string>();

        public override SPHealthCategory Category { get { return SPHealthCategory.Configuration; } }
        public override SPHealthCheckErrorLevel ErrorLevel { get { return SPHealthCheckErrorLevel.Error; } }

        public override SPHealthAnalysisRuleAutomaticExecutionParameters AutomaticExecutionParameters
        {
            get
            {
                return new SPHealthAnalysisRuleAutomaticExecutionParameters()
                {
                    Schedule = SPHealthCheckSchedule.Daily,
                    Scope = SPHealthCheckScope.All,
                    ServiceType = SPFarm.Local.Services.First(s => s.GetType().FullName == "Microsoft.PerformancePoint.Scorecards.BIMonitoringService").GetType(),     // run on any server running PerformancePoint Services
                    RepairAutomatically = false
                };
            }
        }

        public override SPHealthCheckStatus Check()
        {
            var outval = SPHealthCheckStatus.Failed;

            LoggingService.Write(LoggingService.Category_PerformancePoint, TraceSeverity.Verbose, "Executing Health Check", CorrelationToken);

            Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(delegate { outval = doCheck(); });

            LoggingService.Write(
                LoggingService.Category_PerformancePoint,
                TraceSeverity.Verbose,
                String.Format("Health Check completed with outcome {0}", outval.ToString()),
                CorrelationToken);

            return outval;
        } // void Check()

        public override string Summary { get { return "DELEGATION: service account used by PerformancePoint Services not configured for constrained delegation"; } }
        public override string Explanation
        {
            get
            {
                return String.Format(
                    @"{0}PerformancePoint Services delegation requires the service account to use constrained delegation." +
                    @"{0}For more information, see http://technet.microsoft.com/en-us/library/gg502603.aspx" +
                    @"{0}" +
                    @"{0}The following errors were found:" +
                    @"{0}{1}",

                    Environment.NewLine,
                    String.Join(Environment.NewLine, Errors.ToArray())
                    ).Trim();
            }
        }
        public override string Remedy
        {
            get
            {
                return String.Format(
                        @"{0}The following corrective actions are required:" +
                        @"{0}{1}",

                        Environment.NewLine,
                        string.Join(Environment.NewLine, Remedies.ToArray())
                        ).Trim();
            }
        }









        private SPHealthCheckStatus doCheck()
        {
            // load any instances of PerformancePoint Services
            var PerfPointServiceInstances = SPServer.Local
                                                    .ServiceInstances
                                                    .Where(si => si.Service.GetType().Name == "BIMonitoringService")
                                                    .ToList();

            // if no PerformancePoint Service instances exist, automatic pass
            if (!PerfPointServiceInstances.Any())
                return SPHealthCheckStatus.Passed;

            // if no PerformancePoint Service apps exist, automatic pass
            var PerfPointServiceApplications = PerfPointServiceInstances.SelectMany(si => si.Service.Applications).ToList();
            if (!PerfPointServiceApplications.Any())
                return SPHealthCheckStatus.Passed;

            // check each service app
            PerfPointServiceApplications.ForEach(a =>
                {
                    var AccountName = (a as SPIisWebServiceApplication).ApplicationPool.ProcessAccount.LookupName().Split('\\').Last();
                    var DelegationSettings = Helpers.AD_Delegation.GetUser(AccountName);

                    if (DelegationSettings.Permission == AD_Delegation.DelegationPermission.NoDelegation)
                    {
                        Errors.Add(String.Format(@"PerformancePoint Services using service account [{0}] is not configured for delegation", AccountName));
                        Remedies.Add(String.Format(@"Enable delegation by adding an SPN to the service account (SPN does not need to be valid)"));
                        Remedies.Add(String.Format(@"Configure account to use CONSTRAINED delegation WITH protocol transition ('Use any authentication protocol')"));
                    }
                    else if (DelegationSettings.Permission == AD_Delegation.DelegationPermission.Unconstrained)
                    {
                        Errors.Add(String.Format(@"PerformancePoint Services using service account [{0}] is not configured for constrained delegation", AccountName));
                        Remedies.Add(String.Format(@"Reconfigure delegation to use CONSTRAINED delegation WITH protocol transition ('Use any authentication protocol')"));
                    }
                    else if (DelegationSettings.Permission == AD_Delegation.DelegationPermission.Constrained_KerbOnly)
                    {
                        Errors.Add(String.Format(@"PerformancePoint Services using service account [{0}] is not configured for constrained delegation with protocol transition", AccountName));
                        Remedies.Add(String.Format(@"Reconfigure constrained delegation to use protocol transition ('Use any authentication protocol')"));
                    }
                });

            return Errors.Any() ? SPHealthCheckStatus.Failed : SPHealthCheckStatus.Passed;

        } // doCheck()


    }
}
